package com.xsq.wifi.utils;

import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.charset.StandardCharsets;

public class PCPFileTransfer {
    private static final int DEVICE_PORT = 3333;
    private static final int TIMEOUT_MS = 30000;
    private static final byte PROTOCOL_VERSION = 0x01;
    private static final short PCP_STA_OK = 0x0000;
    private static final int MAX_CHUNK_SIZE = 1440 * 2; // 分块大小
    private static final byte FILE_CMD = 0x31; // 唯一指令码

    public static class Response {
        public final byte version;
        public final byte cmd;
        public final short status;
        public final byte[] data;

        public Response(byte version, byte cmd, short status, byte[] data) {
            this.version = version;
            this.cmd = cmd;
            this.status = status;
            this.data = data;
        }
    }

    /**
     * 单指令文件传输
     * @param deviceIp 目标IP
     * @param file 待传输文件
     * @param isLittleEndian 字节序
     * @param callback 进度回调 (0-100)
     */
    public static Response sendFile(String deviceIp, File file,
                                    boolean isLittleEndian,
                                    ProgressCallback callback) throws IOException {
        Socket socket = null;
        try {
            // 1. 建立连接
            socket = new Socket();
            socket.connect(new InetSocketAddress(deviceIp, DEVICE_PORT), TIMEOUT_MS);
            socket.setSoTimeout(TIMEOUT_MS);
            OutputStream out = socket.getOutputStream();
            InputStream in = socket.getInputStream();

            // 2. 发送文件头（0x31指令）
            ByteBuffer metaBuffer = ByteBuffer.allocate(8)
                    .order(isLittleEndian ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN)
                    .put(PROTOCOL_VERSION)
                    .put(FILE_CMD)
                    .putShort((short)0x0000) // 保留字段
                    .putInt(file.getName().getBytes(StandardCharsets.UTF_8).length);

            out.write(metaBuffer.array());
            out.write(file.getName().getBytes(StandardCharsets.UTF_8));
            out.flush();

            // 3. 等待初始响应
            Response ack = readResponse(in, isLittleEndian);
            if (ack.status != PCP_STA_OK) {
                throw new IOException("Server rejected: " + ack.status);
            }

            // 4. 分块发送文件内容（仍用0x31指令）
            try (FileInputStream fis = new FileInputStream(file)) {
                byte[] chunk = new byte[MAX_CHUNK_SIZE];
                long totalSent = 0;
                long fileSize = file.length();

                int bytesRead;
                while ((bytesRead = fis.read(chunk)) != -1) {
                    // 直接发送数据块（无额外协议头）
                    out.write(chunk, 0, bytesRead);
                    out.flush();

                    // 进度回调
                    totalSent += bytesRead;
                    if (callback != null) {
                        callback.onProgress((int)(totalSent * 100 / fileSize));
                    }
                }
            }

            // 5. 获取最终响应
            return readResponse(in, isLittleEndian);
        } finally {
            closeSocket(socket);
        }
    }

    //=============== 核心工具方法 ===============//
    private static Response readResponse(InputStream in, boolean isLittleEndian) throws IOException {
        byte[] header = readExactly(in, 8);
        ByteBuffer buffer = ByteBuffer.wrap(header)
                .order(isLittleEndian ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);

        byte version = buffer.get();
        byte cmd = buffer.get();
        short status = buffer.getShort();
        int dataLength = buffer.getInt();

        if (version != PROTOCOL_VERSION || cmd != FILE_CMD) {
            throw new IOException("Protocol violation");
        }

        byte[] data = dataLength > 0 ? readExactly(in, dataLength) : new byte[0];
        return new Response(version, cmd, status, data);
    }

    private static byte[] readExactly(InputStream in, int length) throws IOException {
        byte[] data = new byte[length];
        int offset = 0;
        while (offset < length) {
            int read = in.read(data, offset, length - offset);
            if (read == -1) throw new EOFException("Connection closed");
            offset += read;
        }
        return data;
    }

    private static void closeSocket(Socket socket) {
        try {
            if (socket != null && !socket.isClosed()) {
                socket.close();
            }
        } catch (IOException e) {
            System.err.println("Socket close error: " + e.getMessage());
        }
    }

    public interface ProgressCallback {
        void onProgress(int percent);
    }
}